
package com.rzico.core.plugin.auth;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.rzico.core.entity.SysPlugin;
import com.rzico.core.model.PluginAttribute;
import com.rzico.core.plugin.AuthPlugin;
import com.rzico.util.RedisHandler;
import com.rzico.util.Sha1Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.net.URLEncoder;
import java.util.*;

/**
 * Plugin - 微信公众号授权
 * 
 * @author RZICO.BOOT
 * @version 3.0
 */
@Component("weixinOAuth2Plugin")
public class weixinOAuth2Plugin extends AuthPlugin {
	protected Logger logger = LoggerFactory.getLogger(this.getClass());

	@Autowired
	private RedisHandler redisHandler;

	// 获取access_token的接口地址（GET） 限200（次/天） 
	private static String access_token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";

	private static String GETJSAPITICKET = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";

	// 用户同意授权，获取code
	private static String oauth2_get_code = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=123#wechat_redirect";
	// 获取oauth2 access_token的接口地址（GET） 限200（次/天） 
	private static String oauth2_access_token_url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";

	// 使用access_token获取用户信息
	private static String user_getinfo_byCode_url = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";

	// 创建永久二维码
	private static String create_qrcode = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=ACCESS_TOKEN";

	@Override
	public String getName() {
		return "微信公众号";
	}

	@Override
	public String getVersion() {
		return "3.8.0";
	}

	@Override
	public List<PluginAttribute> getAttributeKeys() {
		List<PluginAttribute> data = new ArrayList<>();
		data.add(new PluginAttribute("appid","appid",PluginAttribute.STRING_VALUE));
		data.add(new PluginAttribute("appSecret","appSecret",PluginAttribute.STRING_VALUE));
		return data;
	}


	@Override
	public String getOAuth2ByCode(SysPlugin sysPlugin,String redirectUri,String scope) throws Exception {
		return oauth2_get_code
				.replace("APPID", sysPlugin.getAttribute("appid"))
				.replace("REDIRECT_URI", URLEncoder.encode(redirectUri, "utf-8"))
				.replace("SCOPE", scope);
	}

	@Override
	public Map<String,String> getOauth2AccessToken(SysPlugin sysPlugin,String auth_code) throws Exception {

		String requestUrl = oauth2_access_token_url.replace("APPID", sysPlugin.getAttribute("appid")).replace("SECRET", sysPlugin.getAttribute("appSecret")).replace("CODE", auth_code);
		System.out.println(requestUrl);
		try {
			String resultJson = get(requestUrl, null);
			System.out.println(resultJson);
			JSONObject jsonObject = JSON.parseObject(resultJson);
			Map<String,String> data = new HashMap<>();
			data.put("token",jsonObject.getString("access_token"));
			data.put("userId",jsonObject.getString("openid"));
			if (jsonObject.containsKey("unionid")) {
				data.put("unionid", jsonObject.getString("unionid"));
			}
		    return data;
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}

		return null;

	}

	@Override
	public Map<String,String> getUserInfoByToken(SysPlugin sysPlugin,String token,String userId,String encryptData,String iv,String sessionKey) throws Exception {
		String requestUrl = user_getinfo_byCode_url.replace("ACCESS_TOKEN", token).replace("OPENID", userId);

		String resultJson = get(requestUrl,null);

		JSONObject jsonObject = JSON.parseObject(resultJson);
		Map<String,String> data = new HashMap<>();
		data.put("nickname",jsonObject.getString("nickname"));
		data.put("avatar",jsonObject.getString("headimgurl"));
		return data;
	}


	/**
	 * 连接Map键值对
	 * @param map Map
	 * @return 字符串
	 */

	private String joinKeyValue(HashMap<String, Object> map) {
		String buff = "";
		List<HashMap.Entry<String, Object>> infoIds = new ArrayList<HashMap.Entry<String, Object>>(map.entrySet());

		Collections.sort(infoIds, new Comparator<Map.Entry<String, Object>>() {
			public int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2) {
				return (o1.getKey()).toString().compareTo(o2.getKey());
			}
		});
		for (int i = 0; i < infoIds.size(); i++) {
			Map.Entry<String, Object> item = infoIds.get(i);
			String key = item.getKey();
			String val = (String) item.getValue();
			if (null != val && !"".equals(val) && !"sign".equals(key) && !"key".equals(key)) {
				buff += key + "=" + val + "&";

			}
		}

		if (buff.isEmpty() == false) {
			buff = buff.substring(0, buff.length() - 1);
		}
		return buff;
	}

	/**
	 * 作用：生成签名
	 */
	private String getSha1Sign(HashMap<String, Object> params, SysPlugin sysPlugin) throws Exception {
		String str = joinKeyValue(params);
		return Sha1Util.encode(str);
	}

	private String getApiAccessToken(SysPlugin sysPlugin) {

		String apiToken = (String )redisHandler.get("weixin.accessToken."+sysPlugin.getAttribute("appid"));

		if (apiToken!=null) {
			return apiToken;
		}

		String requestUrl = access_token_url.replace("APPID", sysPlugin.getAttribute("appid")).replace("APPSECRET", sysPlugin.getAttribute("appSecret"));
		String resultJson = get(requestUrl, null);

		JSONObject jsonObject = JSON.parseObject(resultJson);
		if (jsonObject.containsKey("access_token")) {
			apiToken = jsonObject.getString("access_token");
			if (apiToken != null) {
				redisHandler.set("weixin.accessToken." + sysPlugin.getAttribute("appid"), apiToken, jsonObject.getLong("expires_in")-60L);
			}
		}

		return apiToken;

	}

	@Override
	public Map<String,String> getConfig(SysPlugin sysPlugin,String url) throws Exception {

		String noncestr = com.rzico.util.StringUtils.CreateNoncestr();
		String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
		HashMap<String, String> map = new HashMap<>();

		String ticket = (String )redisHandler.get("weixin.ticket."+sysPlugin.getAttribute("appid"));

		if (ticket==null) {
			String apiToken = getApiAccessToken(sysPlugin);

			String ticketUrl = GETJSAPITICKET.replace("ACCESS_TOKEN",apiToken);
			String ticketJson = get(ticketUrl, null);

			JSONObject ticketObject = JSON.parseObject(ticketJson);
			ticket = ticketObject.getString("ticket");

			redisHandler.set("weixin.ticket."+sysPlugin.getAttribute("appid"),ticket,ticketObject.getLong("expires_in")-60L);
		}

		if (ticket == null) {
			map.put("return_code","0003");
			map.put("error_msg","参数设置错");
			return map;
		}

		map.put("nonceStr", noncestr);
		map.put("timestamp", timeStamp);
		map.put("url", url);
		map.put("appId", sysPlugin.getAttribute("appid"));

		HashMap<String, Object> signMap = new HashMap<>();
		signMap.put("noncestr",noncestr);
		signMap.put("jsapi_ticket",ticket);
		signMap.put("timestamp",timeStamp);
		signMap.put("url",url);

		String sha1Sign = getSha1Sign(signMap,sysPlugin);

		map.put("signature", sha1Sign);
		map.put("return_code","0000");

		return map;

	}


	@Override
	public Map<String,String> createQrcode(SysPlugin sysPlugin,String scene_id) {
		Map<String,Object> pv = new HashMap<>();
		pv.put("expire_seconds",30*60);
		pv.put("action_name","QR_SCENE");
		Map<String,Object> sc = new HashMap<>();
		pv.put("action_info",sc);
		Map<String,Object> scid = new HashMap<>();
		sc.put("scene",scid);
		scid.put("scene_id",scene_id);

		Map<String,String> map = new HashMap<>();

		// 调用接口获取access_token
		String apiToken = getApiAccessToken(sysPlugin);

		if (null != apiToken) {

			String url = create_qrcode.replace("ACCESS_TOKEN", apiToken);
			// 创建永久二维码
			String resultStr = post(url,JSON.toJSONString(pv));
			JSONObject jsonObject = JSON.parseObject(resultStr);
			if (jsonObject.containsKey("ticket")) {
				map.put("qrcode","https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket="+URLEncoder.encode(jsonObject.getString("ticket")));
				map.put("return_code","0000");
			} else {
				map.put("return_code","0003");
				map.put("error_msg","创建二维码失败");
			}
			return map;
		} else {
			map.put("return_code","0003");
			map.put("error_msg","获取无效令牌");
			return map;
		}

	}

}